package com.ai.module.order.service.impl;

import cn.hutool.http.HttpUtil;
import com.ai.module.chat.common.base.ThreadLocalContext;
import com.ai.module.chat.common.constant.RedisKeyStringConstant;
import com.ai.module.chat.config.advice.exception.BusinessException;
import com.ai.module.chat.config.properties.WxPayConfig;
import com.ai.module.mj.constant.stringConstant.ReturnCode;
import com.ai.module.order.config.WeChatConfig;
import com.ai.module.order.constant.WeChatPayUrl;
import com.ai.module.order.domain.entity.WXOrderVo;
import com.ai.module.order.domain.entity.WxOrderEntity;
import com.ai.module.order.domain.req.OrderReq;
import com.ai.module.order.domain.req.PayCallBackReq;
import com.ai.module.order.domain.req.WxPayBo;
import com.ai.module.order.domain.req.WxPayOrderReq;
import com.ai.module.order.domain.vo.BusinessServiceVo;
import com.ai.module.order.domain.vo.PayInfoVo;
import com.ai.module.order.domain.vo.WeChatPayStatus;
import com.ai.module.order.en.OrderStatus;
import com.ai.module.order.en.UserServiceType;
import com.ai.module.order.mapper.BusinessServiceMapper;
import com.ai.module.order.mapper.WxOrderMapper;
import com.ai.module.order.service.UserOrderService;
import com.ai.module.order.service.WxOrderService;
import com.ai.module.order.util.WxPaySearchOrderUtil;
import com.ai.module.user.constant.WXConstant;
import com.ai.module.chat.util.DateUtils;
import com.ai.module.chat.util.RedisTemplateUtils;
import com.ai.module.mj.util.SnowFlakeUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author: MrHe
 * @create: 2024-02-05 00:12
 * @description:
 **/
@Service
@Slf4j
public class WxOrderServiceImpl extends ServiceImpl<WxOrderMapper, WxOrderEntity> implements WxOrderService {

    @Autowired
    private BusinessServiceMapper businessServiceMapper;

    @Resource
    private RestTemplate weChatRestTemplate;

    @Autowired
    private Verifier verifier;

    @Autowired
    private WxPayConfig wxPayConfig;

    @Resource
    private CloseableHttpClient wxPayClient;

    @Override
    public WxOrderEntity createOrder(OrderReq orderReq) {
        // 生成用户订单信息
        UserServiceType serviceType = orderReq.getServiceType();
        Integer amount = orderReq.getAmount();
        WxOrderEntity wxOrderEntity = new WxOrderEntity();
        wxOrderEntity.setGoodsDesc(serviceType.getDesc());
        String orderPre = StringPool.EMPTY;
        switch (serviceType) {
            case POINTS:
                orderPre = "points_";
                break;
            default:
                wxOrderEntity.setAmount(serviceType.getPoints());
                orderPre = "vip_";
        }
        String orderId = orderPre + SnowFlakeUtil.INSTANCE.nextId();
        wxOrderEntity.setOrderId(orderId);
        wxOrderEntity.setOrderStatus(OrderStatus.CREATED);
        wxOrderEntity.setGoodsDesc(serviceType.getDesc());
        wxOrderEntity.setUserId(ThreadLocalContext.getUserId());
        wxOrderEntity.setServiceType(serviceType);
        this.save(wxOrderEntity);
        String serviceTypeKey = String.format(RedisKeyStringConstant.WE_CHAT_payServiceType, orderId);
        WXOrderVo wxOrderVo = new WXOrderVo();
        BeanUtils.copyProperties(wxOrderEntity,wxOrderVo );
        RedisTemplateUtils.set(serviceTypeKey,wxOrderVo,DateUtils.TIME_HOUR);
        return wxOrderEntity;

    }

    @Override
    public List<BusinessServiceVo> serviceInfo() {
        return businessServiceMapper.serviceInfo();


    }

    @Override
    public PayInfoVo wxPay(WxPayOrderReq wxPayOrderReq) {
        // 调用微信 jsApi生成 支付订单
        WxPayBo wxPayBo = new WxPayBo();
        String orderId = wxPayOrderReq.getOrderId();
//        Integer amount = wxPayOrderReq.getAmount();
        String code = wxPayOrderReq.getCode();
        JSONObject loginInfoObject = getOpenIdByCode(code);
//        String accessToken = loginInfoObject.getString("access_token");
        String openId = "owAHI69stmu0kOxhnyZWs9koeZqQ";
        if (StringUtils.isNotBlank(code)){
            openId = loginInfoObject.getString("openid");
            if (StringUtils.isNotBlank(openId)){
                String userOpenIdKey = String.format(RedisKeyStringConstant.WE_CHAT_User_OpenPid, ThreadLocalContext.getUserId());
                RedisTemplateUtils.set(userOpenIdKey,openId,DateUtils.WEK_DAY);
            }
        }
        WxOrderEntity wxOrder = getOne(new LambdaQueryWrapper<WxOrderEntity>()
                .eq(WxOrderEntity::getOrderId, orderId));
        if (wxOrder == null) {
            throw new BusinessException("用户订单信息不存在");
        }
//        if (wxOrder.getAmount() != amount) {
//            throw new BusinessException("订单金额不符");
//        }
        wxPayBo.setAppid(wxPayConfig.getAppId());
        wxPayBo.setMchid(wxPayConfig.getMchId());
        wxPayBo.setAmount(new WxPayBo.Amount(1));
        wxPayBo.setDescription(wxOrder.getGoodsDesc());
        wxPayBo.setPayer(new WxPayBo.Payer(openId));
        String payCallbackUrl = wxPayConfig.getPayCallbackUrl();
//        String realPayUrl = String.format("%s?orderId=%s", payCallbackUrl, orderId);
        wxPayBo.setNotify_url(payCallbackUrl);
        wxPayBo.setOut_trade_no(orderId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.set("Content-Type", "application/json");
        HttpEntity requestEntity = new HttpEntity(JSONObject.toJSONString(wxPayBo), requestHeaders);

        JSONObject wxPayResponse = weChatRestTemplate
                .postForEntity(
                        WeChatPayUrl.JS_API_PAY,
                        requestEntity,
                        JSONObject.class).getBody();
        String prePayId = wxPayResponse.getString("prepay_id");
        if (StringUtils.isNotBlank(prePayId)) {
            wxOrder.setPrepayId(prePayId);
            updateById(wxOrder);
            // 需要的全部请求参数
            PayInfoVo payInfoVo = new PayInfoVo();
            long currentTimestamp = wxPayConfig.getCurrentTimestamp();
            String nonceStr = wxPayConfig.getNonceStr();
            String paySign = null;//签名
            try {
                paySign = wxPayConfig.getSign(currentTimestamp,nonceStr,String.format("prepay_id=%s",prePayId));
                payInfoVo.setAppId(wxPayConfig.getAppId());
                payInfoVo.setNonceStr(nonceStr);
                payInfoVo.setPaySign(paySign);
                payInfoVo.setPrepayId(prePayId);
                payInfoVo.setTimeStamp(currentTimestamp);
                // reddis 缓存用户 openpId
                return payInfoVo;
            } catch (Exception e) {
                e.printStackTrace();
                log.error("微信支付签名失败 wxPayOrderReq [{}] ,e[{}],wxPayResponse [{}]",wxPayOrderReq,e,wxPayResponse);
                throw new RuntimeException(e);
            }

        }

        return null;

    }

    @Override
    @Deprecated
    public HashMap<String, Object> notifyOrder(JSONObject wxNotify, HttpServletRequest request) {
        /**
         * {
         *     "id": "EV-2018022511223320873",
         *     "create_time": "2015-05-20T13:29:35+08:00",
         *     "resource_type": "encrypt-resource",
         *     "event_type": "TRANSACTION.SUCCESS",
         *     "summary": "支付成功",
         *     "resource": {
         *         "original_type": "transaction",
         *         "algorithm": "AEAD_AES_256_GCM",
         *         "ciphertext": "",
         *         "associated_data": "",
         *         "nonce": ""
         *     }
         * }
         */

        // 数据流
        String body = getRequestBody(request);
        // 随机串
        String nonceStr = request.getHeader("Wechatpay-Nonce");
        // 微信传递的签名
        String signature = request.getHeader("Wechatpay-Signature");
        // 证书序列号
        String serialNo = request.getHeader("Wechatpay-Serial");
        // 时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");

        // 构造签名串
        // 格式   应答时间戳\n
        //       应答随机串\n
        //       时间戳\n
        String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));

        HashMap<String, Object> map = new HashMap<>(2);
        // 验证签名串
        try {
            // 验证签名是否通过
            boolean result = verifiedSign(serialNo, signStr, signature);
            // 解密数据
            if (result) {
                String plainBody = decryptBody(body);
                Map<String, String> paramsMap = convertWechatPayToMap(plainBody);
                // 逻辑处理
//                productOrderService.processOrderCallBackMsg(ProductOrderPayTypeEnum.WECHAT_PAY, paramsMap);
                log.info("微信回调数据 paramsMap[{}]", paramsMap);
                // 响应微信
                map.put("code", "SUCCESS");
                map.put("message", "成功");
            }
        } catch (Exception e) {
            log.error("微信支付回调异常", e);
        }
        return map;
    }

    private Map<String, String> convertWechatPayToMap(String plainBody) {
        Map<String, String> paramsMap = new HashMap<>(2);
        JSONObject jsonObject = JSONObject.parseObject(plainBody);
        // 商户订单号
        paramsMap.put("out_trade_no", jsonObject.getString("out_trade_no"));
        // 交易状态
        paramsMap.put("trade_state", jsonObject.getString("trade_state"));
        //
        paramsMap.put("account_no", jsonObject.getJSONObject("attach").getString("accountNo"));
        return paramsMap;
    }

    private boolean verifiedSign(String serialNo, String signStr, String signature) {
        return verifier.verify(serialNo, signStr.getBytes(StandardCharsets.UTF_8), signature);
    }

    private String getRequestBody(HttpServletRequest request) {
        StringBuffer sb = new StringBuffer();
        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("数据流异常e:", e);
        }
        return sb.toString();
    }

    @Autowired
    private WeChatConfig weChatConfig;

    private String decryptBody(String body) throws GeneralSecurityException {
        AesUtil aesUtil = new AesUtil(weChatConfig.getAppV3Key().getBytes(StandardCharsets.UTF_8));
        JSONObject jsonObject = JSONObject.parseObject(body);
        JSONObject resource = jsonObject.getJSONObject("resource");
        String ciphertext = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonce = resource.getString("nonce");
        return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
    }

    /**
     * 微信根据code获取openId
     * HttpClientUtil请求工具类
     */
    private JSONObject getOpenIdByCode(String code) {
        long start = System.currentTimeMillis();
        //https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code
        String token_url = String.format("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", WXConstant.APPID, WXConstant.SECRET, code);
        JSONObject loginInfoObject = JSON.parseObject(HttpUtil.get(token_url));

        //根据openId和access_token获取用户头像和昵称
        // JSONObject userInfoJson = getUerInfoByWx(accessToken, openId, start);
        return loginInfoObject;
    }

    public static JSONObject getUserInfo(String code) {
        long start = System.currentTimeMillis();
        //https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code
        // https://api.weixin.qq.com/sns/jscode2session
        String token_url = String.format("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", WXConstant.APPID, WXConstant.SECRET, code);
        //String token_url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", WXConstant.APPID, WXConstant.SECRET,code);
        JSONObject loginInfoObject = JSON.parseObject(HttpUtil.get(token_url));
        String accessToken = loginInfoObject.getString("access_token");
        String openId = loginInfoObject.getString("openid");
        //根据openId和access_token获取用户头像和昵称
        JSONObject userInfoJson = getUerInfoByWx(accessToken, openId, start);
        return userInfoJson;
    }

    @NotNull
    public static JSONObject getUerInfoByWx(String accessToken, String openId, long start) {
        String userInfoUrl = String.format("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN", accessToken, openId);
        String userInfoR = HttpUtil.get(userInfoUrl);
        JSONObject userInfoJson = JSON.parseObject(userInfoR);
        log.info("获取到的用户信息为[{}],耗时为[{}]", userInfoJson, System.currentTimeMillis() - start);
        if (userInfoJson.get("errcode") != null) {
            log.error("获取用户信息失败" + userInfoJson.get("errcode").toString() + ":" + userInfoJson.get("errmsg"));

        }
        String nickname = userInfoJson.getString("nickname");
        String headimgurl = userInfoJson.getString("headimgurl");
        log.info("用户昵称:[{}], 用户头像[{}]", nickname, headimgurl);
        return userInfoJson;
    }

    @Override
    public void test(String code) {
        JSONObject openIdByCode = getOpenIdByCode(code);
        System.out.println("openIdByCode = " + openIdByCode);
    }

    @Autowired
    private UserOrderService userOrderService;

    @Override
    public WeChatPayStatus payStatus(String orderId) {
        String orderStatusKey = String.format(RedisKeyStringConstant.WE_CHAT_payStatus, orderId);
        // 查询订单
        if (RedisTemplateUtils.hasKey(orderStatusKey)) {
            return RedisTemplateUtils.get(orderStatusKey);
        }
        WeChatPayStatus weChatPayStatus = WxPaySearchOrderUtil
                .searchByOrderId(wxPayConfig,
                orderId,
                wxPayClient
        );
        if (weChatPayStatus==null){
            throw new BusinessException("订单状态不存在", ReturnCode.FAILURE);
        }
        OrderStatus orderStatus = weChatPayStatus.getOrderStatus();
        switch (orderStatus) {
            case SUCCESS:
            case CLOSED:
                // 将数据保存,修改 订单状态
                RedisTemplateUtils.set(orderStatusKey, weChatPayStatus, DateUtils.TIME_HOUR);
                // 修改 数据库订单状态
                WxOrderEntity wxOrder = new WxOrderEntity();
                wxOrder.setOrderStatus(orderStatus);
                update(wxOrder, new LambdaQueryWrapper<WxOrderEntity>()
                        .eq(WxOrderEntity::getOrderId, orderId));
                if (orderStatus == OrderStatus.SUCCESS) {
                    userOrderService.saveUserOrder(
                            weChatPayStatus
                            );
                }
        }
        return weChatPayStatus;
    }

    @Override
    public Map<String, Object> notifyOrder(PayCallBackReq payCallBack) {
        return callBack(payCallBack);
    }


    public Map<String, Object> callBack(PayCallBackReq payCallBack) {

        try {
            String apiV3Key = wxPayConfig.getApiV3Key();
            PayCallBackReq.BackResource resource = payCallBack.getResource();
            String associated_data = resource.getAssociated_data();
            String ciphertext = resource.getCiphertext();
            String nonce = resource.getNonce();

            String decryptData = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
            //验签成功
            dealCallBackByDecrypt(decryptData);
            /*
    {
        "sp_appid":"wx8888888888888888",
        "sp_mchid":"1230000109",
        "sub_appid":"wxd678efh567hg6999",
        "sub_mchid":"1900000109",
        "out_trade_no":"1217752501201407033233368018",
        "trade_state_desc":"支付成功",
        "trade_type":"MICROPAY",
        "attach":"自定义数据",
        "transaction_id":"1217752501201407033233368018",
        "trade_state":"SUCCESS",
        "bank_type":"CMC",
        "success_time":"2018-06-08T10:34:56+08:00",
        "amount":{
            "payer_total":100,
            "total":100,
            "currency":"CNY",
            "payer_currency":"CNY"
        },
        "promotion_detail":[
        {
            "amount":100,
            "wechatpay_contribute":0,
            "coupon_id":"109519",
            "scope":"GLOBAL",
            "merchant_contribute":0,
            "name":"单品惠-6",
            "other_contribute":0,
            "currency":"CNY",
            "stock_id":"931386",
            "goods_detail":[
                {
                    "goods_remark":"商品备注信息",
                    "quantity":1,
                    "discount_amount":1,
                    "goods_id":"M1006",
                    "unit_price":100
                }
            ]
        }
    ],
    "payer":{
        "openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
    },
    "scene_info":{
        "device_id":"013467007045764"
    }
}
            * */
        } catch (Exception e) {
            log.info("{} ,parms{}, 异常:",   payCallBack, e);
        }

        Map<String, Object> res = new HashMap<>();
        res.put("code", "SUCCESS");
        res.put("message", "成功");
        return res;
    }

    @Override
    public void dealCallBackByDecrypt(String decryptData) {
        JSONObject decryptDataObj = JSONObject.parseObject(decryptData, JSONObject.class);
        //decryptDataObj 为解码后的obj，其内容如下。之后便是验签成功后的业务处理
        String orderId = decryptDataObj.getString("out_trade_no");
        String successTimeStr = decryptDataObj.getString("success_time");
        String orderStatusKey = String.format(RedisKeyStringConstant.WE_CHAT_payStatus, orderId);
        log.info("接收到微信回调接口数据: [{}]",decryptData);
        String redisKey = String.format(RedisKeyStringConstant.WE_CHAT_order_user, orderId);
        if (! RedisTemplateUtils.hasKey(redisKey)){
            //首次通知
            String tradeStateStr = decryptDataObj.getString("trade_state");
            String tradeType = decryptDataObj.getString("trade_type");
            Transaction.TradeStateEnum tradeState = Transaction.TradeStateEnum.valueOf(tradeStateStr);
            switch (tradeState){
                case CLOSED:
                case SUCCESS:
                case PAYERROR:
                    JSONObject amount = decryptDataObj.getJSONObject("amount");
                    WeChatPayStatus weChatPayStatus = new WeChatPayStatus();
                    weChatPayStatus.setAmount(amount.getInteger("payer_total"));
                    weChatPayStatus.setSuccessTime(successTimeStr);
                    weChatPayStatus.setOrderId(orderId);
                    weChatPayStatus.setTradeType(tradeType);
                    weChatPayStatus.setTransactionId(decryptDataObj.getString("transaction_id"));
                    RedisTemplateUtils.set(orderStatusKey, weChatPayStatus, DateUtils.TIME_HOUR);
                    //   进行 wx_order状态更改
                    // 修改 数据库订单状态
                    WxOrderEntity wxOrder = new WxOrderEntity();
                    wxOrder.setOrderStatus(OrderStatus.valueOf(tradeStateStr));
                    update(wxOrder, new LambdaQueryWrapper<WxOrderEntity>()
                            .eq(WxOrderEntity::getOrderId, orderId));
                    if (tradeState == Transaction.TradeStateEnum.SUCCESS) {
                        userOrderService.saveUserOrder(weChatPayStatus);
                    }

            }
            log.info("接收到微信回调接口解析后的数据: [{}]",decryptDataObj);
        }else {
            RedisTemplateUtils.set(redisKey,decryptDataObj,DateUtils.TIME_DAY) ;
        }

    }
}
